home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / cmdline.lha / cmdline / src / lib / cmdarg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-13  |  9.9 KB  |  368 lines

  1. //------------------------------------------------------------------------
  2. // ^FILE: cmdarg.c - implement a CmdArg
  3. //
  4. // ^DESCRIPTION:
  5. //    This file implements the CmdArg class which is the base
  6. //    class for all command-arguments.
  7. //
  8. // ^HISTORY:
  9. //    03/25/92    Brad Appleton    <brad@ssd.csd.harris.com>    Created
  10. //
  11. //    03/01/93    Brad Appleton    <brad@ssd.csd.harris.com>
  12. //    - Added arg_sequence field to CmdArg
  13. //-^^---------------------------------------------------------------------
  14.  
  15. #include <stdlib.h>
  16. #include <iostream.h>
  17. #include <string.h>
  18. #include <ctype.h>
  19.  
  20. #include "cmdline.h"
  21.  
  22. //---------------------------------------------------------------------- CmdArg
  23.  
  24. int  CmdArg::is_dummy(void) { return  0; }
  25.  
  26.    // Copy-Constructor
  27. CmdArg::CmdArg(const CmdArg & cp)
  28.    : alloc_value_name(cp.alloc_value_name),
  29.      arg_flags(cp.arg_flags),
  30.      arg_syntax(cp.arg_syntax),
  31.      arg_sequence(cp.arg_sequence),
  32.      arg_char_name(cp.arg_char_name),
  33.      arg_keyword_name(cp.arg_keyword_name),
  34.      arg_value_name(cp.arg_value_name),
  35.      arg_description(cp.arg_description)
  36. {
  37.    if (alloc_value_name) {
  38.       char * val_name = new char[::strlen(cp.arg_value_name) + 1] ;
  39.       ::strcpy((char *)val_name, cp.arg_value_name);
  40.       arg_value_name = val_name;
  41.    }
  42. }
  43.  
  44.    // Constructors
  45.  
  46. CmdArg::CmdArg(char         optchar,
  47.                const char * keyword,
  48.                const char * value,
  49.                const char * description,
  50.                unsigned     syntax_flags)
  51.    : alloc_value_name(0),
  52.      arg_flags(0),
  53.      arg_syntax(syntax_flags),
  54.      arg_sequence(0),
  55.      arg_char_name(optchar),
  56.      arg_keyword_name(keyword),
  57.      arg_value_name(value),
  58.      arg_description(description)
  59. {
  60.    parse_description();
  61.    parse_value();
  62.    adjust_syntax();
  63. }
  64.  
  65.  
  66. CmdArg::CmdArg(char         optchar,
  67.                const char * keyword,
  68.                const char * description,
  69.                unsigned     syntax_flags)
  70.    : alloc_value_name(0),
  71.      arg_flags(0),
  72.      arg_syntax(syntax_flags),
  73.      arg_sequence(0),
  74.      arg_char_name(optchar),
  75.      arg_keyword_name(keyword),
  76.      arg_value_name(NULL),
  77.      arg_description(description)
  78. {
  79.    parse_description();
  80.    adjust_syntax();
  81. }
  82.  
  83.  
  84. CmdArg::CmdArg(const char * value,
  85.                const char * description,
  86.                unsigned     syntax_flags)
  87.    : alloc_value_name(0),
  88.      arg_flags(0),
  89.      arg_syntax(syntax_flags),
  90.      arg_sequence(0),
  91.      arg_char_name(0),
  92.      arg_keyword_name(NULL),
  93.      arg_value_name(value),
  94.      arg_description(description)
  95. {
  96.    parse_description();
  97.    parse_value();
  98.    adjust_syntax();
  99. }
  100.  
  101.  
  102.    // Destructor
  103. CmdArg::~CmdArg(void)
  104. {
  105.    if (alloc_value_name)    delete [] (char *)arg_value_name;
  106. }
  107.  
  108.  
  109. //-------------------
  110. // ^FUNCTION: adjust_syntax - adjust command argument syntax
  111. //
  112. // ^SYNOPSIS:
  113. //    CmdArg::adjust_syntax(void)
  114. //
  115. // ^PARAMETERS:
  116. //    None.
  117. //
  118. // ^DESCRIPTION:
  119. //    This routine tries to "iron out" any inconsistencies (such as
  120. //    conflicting syntax flags) in the way a command-argument is specified
  121. //    and makes its best guess at what the user eally intended.
  122. //
  123. // ^REQUIREMENTS:
  124. //    parse_value() and parse_description() must already have been called.
  125. //
  126. // ^SIDE-EFFECTS:
  127. //    Modifies the argument syntax flags.
  128. //    Modifies is keyword and value names if they are ""
  129. //
  130. // ^RETURN-VALUE:
  131. //    None.
  132. //
  133. // ^ALGORITHM:
  134. //    Follow along in the code ...
  135. //-^^----------------
  136. void
  137. CmdArg::adjust_syntax(void)
  138. {
  139.    static const char default_value_name[] = "value" ;
  140.  
  141.       // If the value is specified as both OPTIONAL and REQUIRED
  142.       // then assume it is required.
  143.       //
  144.    if ((arg_syntax & isVALREQ) && (arg_syntax & isVALOPT)) {
  145.       arg_syntax &= ~isVALOPT ;
  146.    }
  147.  
  148.       // If they said the argument was both STICKY and SEPARATE then
  149.       // I dont know what to think just just ignore both of them.
  150.       //
  151.    if ((arg_syntax & isVALSTICKY) && (arg_syntax & isVALSEP)) {
  152.       arg_syntax &= ~(isVALSTICKY | isVALSEP);
  153.    }
  154.  
  155.       // If a non-NULL, non-empty value-name was given but we werent
  156.       // told that the argument takes a value, then assume that it
  157.       // does take a value and that the value is required.
  158.       //
  159.    if (arg_value_name && *arg_value_name && (! (arg_syntax & isVALTAKEN))) {
  160.       arg_syntax |= isVALREQ;
  161.    }
  162.  
  163.       // If a value is taken and the argument is positional, then
  164.       // we need to make isREQ and isOPT consistent with isVALREQ
  165.       // and isVALOPT
  166.       //
  167.    if ((arg_syntax & isVALTAKEN) && (arg_syntax & isPOS)) {
  168.       arg_syntax &= ~(isREQ | isOPT);
  169.       if (arg_syntax & isVALREQ)  arg_syntax |= isREQ;
  170.       if (arg_syntax & isVALOPT)  arg_syntax |= isOPT;
  171.    }
  172.  
  173.       // If the keyword name is empty then just use NULL
  174.    if (arg_keyword_name  &&  (! *arg_keyword_name)) {
  175.       arg_keyword_name = NULL;
  176.    }
  177.  
  178.       // If the value name is empty then just use NULL
  179.    if (arg_value_name  &&  (! *arg_value_name)) {
  180.       arg_value_name = NULL;
  181.    }
  182.  
  183.       // If a value is taken but no value name was given,
  184.       // then default the value name.
  185.       //
  186.    if ((arg_syntax & isVALTAKEN) && (! arg_value_name)) {
  187.       arg_value_name = default_value_name;
  188.    }
  189.  
  190.       // If no keyword name or character name was given, then the
  191.       // argument had better take a value and it must be positional
  192.       //
  193.    if ((! arg_char_name) && (arg_keyword_name == NULL) &&
  194.                             (! (arg_syntax & isPOS))) {
  195.       if (arg_syntax & isVALTAKEN) {
  196.          arg_syntax |= isPOS;
  197.       } else {
  198.          cerr << "*** Error: non-positional CmdArg "
  199.               << "has no character or keyword name!\n"
  200.               << "\t(error occurred in CmdArg constructor)" << endl ;
  201.       }
  202.    }
  203. }
  204.  
  205.  
  206. //-------------------
  207. // ^FUNCTION: parse_description - parse the argument description string
  208. //
  209. // ^SYNOPSIS:
  210. //    CmdLine::parse_description(void)
  211. //
  212. // ^PARAMETERS:
  213. //    None.
  214. //
  215. // ^DESCRIPTION:
  216. //    All we have to do is see if the first non-white character of
  217. //    the description is string is ';'. If it is, then the argument
  218. //    is a "hidden" argument and the description starts with the
  219. //    next non-white character.
  220. //
  221. // ^REQUIREMENTS:
  222. //    None.
  223. //
  224. // ^SIDE-EFFECTS:
  225. //    Modifies arg_description
  226. //
  227. // ^RETURN-VALUE:
  228. //    None.
  229. //
  230. // ^ALGORITHM:
  231. //    Trivial.
  232. //-^^----------------
  233. enum { c_HIDDEN = ';', c_OPEN = '[', c_CLOSE = ']', c_LIST = '.' } ;
  234.  
  235. void
  236. CmdArg::parse_description(void)
  237. {
  238.    if (arg_description == NULL)  return;
  239.    while (isspace(*arg_description))  ++arg_description;
  240.    if (*arg_description == c_HIDDEN) {
  241.       arg_syntax |= isHIDDEN ;
  242.       ++arg_description;
  243.       while (isspace(*arg_description))  ++arg_description;
  244.    }
  245. }
  246.  
  247.  
  248. //-------------------
  249. // ^FUNCTION: parse_value - parse the argument value name
  250. //
  251. // ^SYNOPSIS:
  252. //    CmdLine::parse_value(void)
  253. //
  254. // ^PARAMETERS:
  255. //    None.
  256. //
  257. // ^DESCRIPTION:
  258. //    This routine parses the argument value string. If the value name is
  259. //    is enclosed between '[' and ']', then the value is optional (not
  260. //    required) and we need to modify the arg_syntax flags. Also, if the
  261. //    value name is suffixed by "..." then it means the value is a LIST
  262. //    of values and we need to modify the arg_syntax flags.
  263. //
  264. // ^REQUIREMENTS:
  265. //    This routine must be called BEFORE, adjust_syntax() is called/
  266. //
  267. // ^SIDE-EFFECTS:
  268. //    Modifies the arg_value_name and the arg_syntax flags.
  269. //
  270. // ^RETURN-VALUE:
  271. //    None.
  272. //
  273. // ^ALGORITHM:
  274. //    Its kind of hairy so follow along.
  275. //-^^----------------
  276. void
  277. CmdArg::parse_value(void)
  278. {
  279.    const char * save_value = arg_value_name;
  280.    int  brace = 0;
  281.    int  errors = 0;
  282.  
  283.    // Skip whitespace as necessary and look for a '['
  284.    while (isspace(*arg_value_name))  ++arg_value_name;
  285.    if (*arg_value_name == c_OPEN) {
  286.       ++brace;
  287.       ++arg_value_name;
  288.       while (isspace(*arg_value_name))  ++arg_value_name;
  289.       arg_syntax &= ~isVALREQ;
  290.       arg_syntax |= isVALOPT;
  291.    }
  292.  
  293.    // Now that arg_value_name points to the beginning of the value,
  294.    // lets find the end of it.
  295.    //
  296.    const char * ptr = arg_value_name;
  297.    while ((*ptr) && (! isspace(*ptr)) &&
  298.           (*ptr != c_LIST) && (*ptr != c_CLOSE)) {
  299.       ++ptr;
  300.    }
  301.  
  302.    // See if we dont need to allocate a new string
  303.    if ((! *ptr) && (save_value == arg_value_name))  return;
  304.  
  305.    // Copy the value name
  306.    alloc_value_name = 1;
  307.    int  len = (int) (ptr - arg_value_name);
  308.    char * copied_value = new char[len + 1];
  309.    (void) ::strncpy(copied_value, arg_value_name, len);
  310.    copied_value[len] = '\0';
  311.    arg_value_name = copied_value;
  312.  
  313.    // Did we end on a ']' ?
  314.    if (*ptr == c_CLOSE) {
  315.       if (! brace) {
  316.          cerr << "Error: unmatched '" << char(c_CLOSE) << "'." << endl ;
  317.          ++errors;
  318.          arg_syntax &= ~isVALREQ;
  319.          arg_syntax |= isVALOPT;
  320.       }
  321.       brace = 0;
  322.       ++ptr;
  323.    }
  324.  
  325.    // Skip whitespace and see if we are finished.
  326.    while (isspace(*ptr))  ++ptr;
  327.    if (! *ptr) {
  328.       // Was there an unmatched ']'
  329.       if (brace) {
  330.          cerr << "Error: unmatched '" << char(c_OPEN) << "'." << endl ;
  331.          ++errors;
  332.       }
  333.       if (errors) {
  334.          cerr << "*** Syntax error in value \"" << save_value << "\".\n"
  335.               << "\t(error occurred in CmdArg constructor)" << endl ;
  336.       }
  337.       return;
  338.    }
  339.  
  340.    // Not done - we had better see a "..."
  341.    if (::strncmp(ptr, "...", 3) != 0) {
  342.       cerr << "Error: unexpected token \"" << ptr << "\"." << endl ;
  343.       ++errors;
  344.    } else {
  345.       arg_syntax |= isLIST;
  346.       ptr += 3;
  347.       while (isspace(*ptr))  ++ptr;
  348.       if (brace && (*ptr != c_CLOSE)) {
  349.          cerr << "Error: unmatched '" << char(c_OPEN) << "'." << endl ;
  350.          ++errors;
  351.       } else {
  352.         // If theres anything left (except ']') it's an error
  353.         if (brace && (*ptr == c_CLOSE))  ++ptr;
  354.         while (isspace(*ptr))  ++ptr;
  355.         if (*ptr) {
  356.            cerr << "Error: unexpected token \"" << ptr << "\"." << endl ;
  357.            ++errors;
  358.         }
  359.       }
  360.    }
  361.  
  362.    // Were there any errors?
  363.    if (errors) {
  364.       cerr << "*** Syntax error in value \"" << save_value << "\".\n"
  365.            << "\t(error occurred in CmdArg constructor)" << endl ;
  366.    }
  367. }
  368.